#pragma once

#include <iostream>
#include <functional>
#include <pthread.h>
#include "Sock.hpp"
#include "Protocol.hpp"

using namespace std;

namespace tcpserver_ns
{
    using namespace protocol_ns;
    using fun_t = function<Response(const Request &)>;

    class TcpServer;

    struct ThreadData
    {
        int _sock;
        uint16_t _port;
        string _ip;
        TcpServer *_tc;
        ThreadData(int sock, string ip, uint16_t port, TcpServer *tc) : _sock(sock), _ip(ip), _port(port), _tc(tc)
        {
        }
    };
    class TcpServer
    {
    private:
        uint16_t _port;
        Sock _listensock;
        fun_t _func;

    public:
        TcpServer(fun_t func, uint16_t port) : _func(func), _port(port)
        {
        }
        void InitServer()
        {
            _listensock.Socket();
            _listensock.Bind(_port);
            _listensock.Listen();
            logMessage(Info, "init server done,listensock:%d", _listensock.Fd());
        }
        void Start()
        {
            for (;;)
            {
                string clientip;
                uint16_t clientport;
                int sock = _listensock.Accept(&clientip, &clientport);
                if (sock < 0)
                {
                    continue;
                }
                logMessage(Debug, "get a new client, client info : [%s:%d]", clientip.c_str(), clientport);

                pthread_t tid;
                ThreadData *td = new ThreadData(sock, clientip, clientport, this);
                pthread_create(&tid, nullptr, ThreadRoutine, td);
            }
        }
        static void *ThreadRoutine(void *args)
        {
            pthread_detach(pthread_self());
            ThreadData *td = static_cast<ThreadData *>(args);
            td->_tc->ServiceIO(td->_sock, td->_ip, td->_port);
            logMessage(Debug, "thread quit, client quit ...");
            delete td;
            return nullptr;
        }
        void ServiceIO(int sock, const string &ip, const uint16_t &port)
        {
            string inbuffer;

            while (true)
            {
                string package;
                int n = ReadPackage(sock, inbuffer, &package);
                if (n == -1)
                    break;
                else if (n == 0)
                    continue;
                else
                {
                    package = RemoveHeader(package,n);

                    Request req;
                    req.Deserialize(package);

                    Response resp = _func(req);

                    string send_string;
                    resp.Serialize(&send_string);

                    send_string = AddHeader(send_string);
                    send(sock,send_string.c_str(),send_string.size(),0);
                }
            }
            close(sock);
        }
        ~TcpServer()
        {
            _listensock.Close();
        }
    };
}